/*
 ------------------------------------------------------------------------------
 Copyright (C) 2006-2007 Team Blur.

 This file is part of the Quantum Engine source code.

 The Quantum Engine source code is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or (at your
 option) any later version.

 The Quantum Engine source code is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 more details.

 You should have received a copy of the GNU General Public License along with
 the Quantum Engine source code; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 ------------------------------------------------------------------------------
*/


#ifndef __MATH_MATRIX_H__
#define __MATH_MATRIX_H__


extern odMat3		mat3_identity;
extern odMat4		mat4_identity;

/*
 ==============================================================================

 3x3 MATRIX

 ==============================================================================
*/

class odMat3 {
public:
	odVec3			m[3];

					odMat3 (void);
	explicit		odMat3 (const odVec3 &x, const odVec3 &y, const odVec3 &z);
	explicit		odMat3 (float xx, float xy, float xz, float yx, float yy, float yz, float zx, float zy, float zz);

	const odVec3 &	operator [] (int index) const;
	odVec3 &		operator [] (int index);

	odMat3 &		operator = (const odMat3 &mat);
	bool			operator == (const odMat3 &mat) const;
	bool			operator != (const odMat3 &mat) const;

	odMat3			operator * (const odMat3 &mat) const;
	odMat3 &		operator *= (const odMat3 &mat);

	void			Set (const odVec3 &x, const odVec3 &y, const odVec3 &z);
	void			Set (float xx, float xy, float xz, float yx, float yy, float yz, float zx, float zy, float zz);
	void			Identity (void);
	bool			IsIdentity (void) const;
	odMat3			Transpose (void) const;
	void			TransposeSelf (void);
	void			ProjectVector (const odVec3 &in, odVec3 &out) const;
	void			UnprojectVector (const odVec3 &in, odVec3 &out) const;

	const float *	ToFloatPtr (void) const;
	float *			ToFloatPtr (void);

	odAngles		ToAngles (void) const;
	odQuat			ToQuat (void) const;
	odMat4			ToMat4 (void) const;
	const char *	ToString (int precision = 2) const;
};

INLINE odMat3::odMat3 (void){

}

INLINE odMat3::odMat3 (const odVec3 &x, const odVec3 &y, const odVec3 &z){

	m[0] = x;
	m[1] = y;
	m[2] = z;
}

INLINE odMat3::odMat3 (float xx, float xy, float xz, float yx, float yy, float yz, float zx, float zy, float zz){

	m[0].Set(xx, xy, xz);
	m[1].Set(yx, yy, yz);
	m[2].Set(zx, zy, zz);
}

INLINE const odVec3 &odMat3::operator [] (int index) const {

	return m[index];
}

INLINE odVec3 &odMat3::operator [] (int index){

	return m[index];
}

INLINE odMat3 &odMat3::operator = (const odMat3 &mat){

	m[0] = mat[0];
	m[1] = mat[1];
	m[2] = mat[2];

	return *this;
}

INLINE bool odMat3::operator == (const odMat3 &mat) const {

	if (m[0] != mat[0] || m[1] != mat[1] || m[2] != mat[2])
		return false;

	return true;
}

INLINE bool odMat3::operator != (const odMat3 &mat) const {

	if (m[0] == mat[0] && m[1] == mat[1] && m[2] == mat[2])
		return false;

	return true;
}

INLINE odMat3 odMat3::operator * (const odMat3 &mat) const {

	odMat3	result;

	result[0][0] = m[0][0] * mat[0][0] + m[0][1] * mat[1][0] + m[0][2] * mat[2][0];
	result[0][1] = m[0][0] * mat[0][1] + m[0][1] * mat[1][1] + m[0][2] * mat[2][1];
	result[0][2] = m[0][0] * mat[0][2] + m[0][1] * mat[1][2] + m[0][2] * mat[2][2];
	result[1][0] = m[1][0] * mat[0][0] + m[1][1] * mat[1][0] + m[1][2] * mat[2][0];
	result[1][1] = m[1][0] * mat[0][1] + m[1][1] * mat[1][1] + m[1][2] * mat[2][1];
	result[1][2] = m[1][0] * mat[0][2] + m[1][1] * mat[1][2] + m[1][2] * mat[2][2];
	result[2][0] = m[2][0] * mat[0][0] + m[2][1] * mat[1][0] + m[2][2] * mat[2][0];
	result[2][1] = m[2][0] * mat[0][1] + m[2][1] * mat[1][1] + m[2][2] * mat[2][1];
	result[2][2] = m[2][0] * mat[0][2] + m[2][1] * mat[1][2] + m[2][2] * mat[2][2];

	return result;
}

INLINE odMat3 &odMat3::operator *= (const odMat3 &mat){

	odVec3	mx, my, mz;

	mx = m[0];
	my = m[1];
	mz = m[2];

	m[0][0] = mx[0] * mat[0][0] + mx[1] * mat[1][0] + mx[2] * mat[2][0];
	m[0][1] = mx[0] * mat[0][1] + mx[1] * mat[1][1] + mx[2] * mat[2][1];
	m[0][2] = mx[0] * mat[0][2] + mx[1] * mat[1][2] + mx[2] * mat[2][2];
	m[1][0] = my[0] * mat[0][0] + my[1] * mat[1][0] + my[2] * mat[2][0];
	m[1][1] = my[0] * mat[0][1] + my[1] * mat[1][1] + my[2] * mat[2][1];
	m[1][2] = my[0] * mat[0][2] + my[1] * mat[1][2] + my[2] * mat[2][2];
	m[2][0] = mz[0] * mat[0][0] + mz[1] * mat[1][0] + mz[2] * mat[2][0];
	m[2][1] = mz[0] * mat[0][1] + mz[1] * mat[1][1] + mz[2] * mat[2][1];
	m[2][2] = mz[0] * mat[0][2] + mz[1] * mat[1][2] + mz[2] * mat[2][2];

	return *this;
}

INLINE void odMat3::Set (const odVec3 &x, const odVec3 &y, const odVec3 &z){

	m[0] = x;
	m[1] = y;
	m[2] = z;
}

INLINE void odMat3::Set (float xx, float xy, float xz, float yx, float yy, float yz, float zx, float zy, float zz){

	m[0].Set(xx, xy, xz);
	m[1].Set(yx, yy, yz);
	m[2].Set(zx, zy, zz);
}

INLINE void odMat3::Identity (void){

	m[0].Set(1.0f, 0.0f, 0.0f);
	m[1].Set(0.0f, 1.0f, 0.0f);
	m[2].Set(0.0f, 0.0f, 1.0f);
}

INLINE bool odMat3::IsIdentity (void) const {

	if (m[0][0] != 1.0f || m[0][1] != 0.0f || m[0][2] != 0.0f)
		return false;
	if (m[1][0] != 0.0f || m[1][1] != 1.0f || m[1][2] != 0.0f)
		return false;
	if (m[2][0] != 0.0f || m[2][1] != 0.0f || m[2][2] != 1.0f)
		return false;

	return true;
}

INLINE odMat3 odMat3::Transpose (void) const {

	odMat3	transpose;

	transpose[0].Set(m[0][0], m[1][0], m[2][0]);
	transpose[1].Set(m[0][1], m[1][1], m[2][1]);
	transpose[2].Set(m[0][2], m[1][2], m[2][2]);

	return transpose;
}

INLINE void odMat3::TransposeSelf (void){

	float	tmp;

	tmp = m[0][1];
	m[0][1] = m[1][0];
	m[1][0] = tmp;

	tmp = m[0][2];
	m[0][2] = m[2][0];
	m[2][0] = tmp;

	tmp = m[1][2];
	m[1][2] = m[2][1];
	m[2][1] = tmp;
}

INLINE void odMat3::ProjectVector (const odVec3 &in, odVec3 &out) const {

	out.x = in.x * m[0][0] + in.y * m[0][1] + in.z * m[0][2];
	out.y = in.x * m[1][0] + in.y * m[1][1] + in.z * m[1][2];
	out.z = in.x * m[2][0] + in.y * m[2][1] + in.z * m[2][2];
}

INLINE void odMat3::UnprojectVector (const odVec3 &in, odVec3 &out) const {

	out.x = in.x * m[0][0] + in.y * m[1][0] + in.z * m[2][0];
	out.y = in.x * m[0][1] + in.y * m[1][1] + in.z * m[2][1];
	out.z = in.x * m[0][2] + in.y * m[1][2] + in.z * m[2][2];
}

INLINE const float *odMat3::ToFloatPtr (void) const {

	return m[0].ToFloatPtr();
}

INLINE float *odMat3::ToFloatPtr (void){

	return m[0].ToFloatPtr();
}

/*
 ==============================================================================

 4x4 MATRIX

 ==============================================================================
*/

class odMat4 {
public:
	odVec4			m[4];

					odMat4 (void);
	explicit		odMat4 (const odVec4 &x, const odVec4 &y, const odVec4 &z, const odVec4 &w);
	explicit		odMat4 (const odMat3 &rotation, const odVec3 &translation);
	explicit		odMat4 (float xx, float xy, float xz, float xw, float yx, float yy, float yz, float yw, float zx, float zy, float zz, float zw, float wx, float wy, float wz, float ww);

	const odVec4 &	operator [] (int index) const;
	odVec4 &		operator [] (int index);

	odMat4 &		operator = (const odMat4 &mat);
	bool			operator == (const odMat4 &mat) const;
	bool			operator != (const odMat4 &mat) const;

	odMat4			operator * (const odMat4 &mat) const;
	odMat4 &		operator *= (const odMat4 &mat);

	void			Set (const odVec4 &x, const odVec4 &y, const odVec4 &z, const odVec4 &w);
	void			Set (const odMat3 &rotation, const odVec3 &translation);
	void			Set (float xx, float xy, float xz, float xw, float yx, float yy, float yz, float yw, float zx, float zy, float zz, float zw, float wx, float wy, float wz, float ww);
	void			Identity (void);
	bool			IsIdentity (void) const;
	odMat4			Transpose (void) const;
	void			TransposeSelf (void);
	void			Scale (float x, float y, float z);
	void			Translate (float x, float y, float z);
	void			Shear (float x, float y, float z);
	void			Rotate (float angle, float x, float y, float z);
	void			ProjectVector (const odVec4 &in, odVec4 &out) const;
	void			UnprojectVector (const odVec4 &in, odVec4 &out) const;

	const float *	ToFloatPtr (void) const;
	float *			ToFloatPtr (void);

	odMat3			ToMat3 (void) const;
	const char *	ToString (int precision = 2) const;
};

INLINE odMat4::odMat4 (void){

}

INLINE odMat4::odMat4 (const odVec4 &x, const odVec4 &y, const odVec4 &z, const odVec4 &w){

	m[0] = x;
	m[1] = y;
	m[2] = z;
	m[3] = w;
}

INLINE odMat4::odMat4 (const odMat3 &rotation, const odVec3 &translation){

	m[0].Set(rotation[0][0], rotation[0][1], rotation[0][2], 0.0f);
	m[1].Set(rotation[1][0], rotation[1][1], rotation[1][2], 0.0f);
	m[2].Set(rotation[2][0], rotation[2][1], rotation[2][2], 0.0f);
	m[3].Set(translation[0], translation[1], translation[2], 1.0f);
}

INLINE odMat4::odMat4 (float xx, float xy, float xz, float xw, float yx, float yy, float yz, float yw, float zx, float zy, float zz, float zw, float wx, float wy, float wz, float ww){

	m[0].Set(xx, xy, xz, xw);
	m[1].Set(yx, yy, yz, yw);
	m[2].Set(zx, zy, zz, zw);
	m[3].Set(wx, wy, wz, ww);
}

INLINE const odVec4 &odMat4::operator [] (int index) const {

	return m[index];
}

INLINE odVec4 &odMat4::operator [] (int index){

	return m[index];
}

INLINE odMat4 &odMat4::operator = (const odMat4 &mat){

	m[0] = mat[0];
	m[1] = mat[1];
	m[2] = mat[2];
	m[3] = mat[3];

	return *this;
}

INLINE bool odMat4::operator == (const odMat4 &mat) const {

	if (m[0] != mat[0] || m[1] != mat[1] || m[2] != mat[2] || m[3] != mat[3])
		return false;

	return true;
}

INLINE bool odMat4::operator != (const odMat4 &mat) const {

	if (m[0] == mat[0] && m[1] == mat[1] && m[2] == mat[2] && m[3] == mat[3])
		return false;

	return true;
}

INLINE odMat4 odMat4::operator * (const odMat4 &mat) const {

	odMat4	result;

	result[0][0] = m[0][0] * mat[0][0] + m[1][0] * mat[0][1] + m[2][0] * mat[0][2] + m[3][0] * mat[0][3];
	result[0][1] = m[0][1] * mat[0][0] + m[1][1] * mat[0][1] + m[2][1] * mat[0][2] + m[3][1] * mat[0][3];
	result[0][2] = m[0][2] * mat[0][0] + m[1][2] * mat[0][1] + m[2][2] * mat[0][2] + m[3][2] * mat[0][3];
	result[0][3] = m[0][3] * mat[0][0] + m[1][3] * mat[0][1] + m[2][3] * mat[0][2] + m[3][3] * mat[0][3];
	result[1][0] = m[0][0] * mat[1][0] + m[1][0] * mat[1][1] + m[2][0] * mat[1][2] + m[3][0] * mat[1][3];
	result[1][1] = m[0][1] * mat[1][0] + m[1][1] * mat[1][1] + m[2][1] * mat[1][2] + m[3][1] * mat[1][3];
	result[1][2] = m[0][2] * mat[1][0] + m[1][2] * mat[1][1] + m[2][2] * mat[1][2] + m[3][2] * mat[1][3];
	result[1][3] = m[0][3] * mat[1][0] + m[1][3] * mat[1][1] + m[2][3] * mat[1][2] + m[3][3] * mat[1][3];
	result[2][0] = m[0][0] * mat[2][0] + m[1][0] * mat[2][1] + m[2][0] * mat[2][2] + m[3][0] * mat[2][3];
	result[2][1] = m[0][1] * mat[2][0] + m[1][1] * mat[2][1] + m[2][1] * mat[2][2] + m[3][1] * mat[2][3];
	result[2][2] = m[0][2] * mat[2][0] + m[1][2] * mat[2][1] + m[2][2] * mat[2][2] + m[3][2] * mat[2][3];
	result[2][3] = m[0][3] * mat[2][0] + m[1][3] * mat[2][1] + m[2][3] * mat[2][2] + m[3][3] * mat[2][3];
	result[3][0] = m[0][0] * mat[3][0] + m[1][0] * mat[3][1] + m[2][0] * mat[3][2] + m[3][0] * mat[3][3];
	result[3][1] = m[0][1] * mat[3][0] + m[1][1] * mat[3][1] + m[2][1] * mat[3][2] + m[3][1] * mat[3][3];
	result[3][2] = m[0][2] * mat[3][0] + m[1][2] * mat[3][1] + m[2][2] * mat[3][2] + m[3][2] * mat[3][3];
	result[3][3] = m[0][3] * mat[3][0] + m[1][3] * mat[3][1] + m[2][3] * mat[3][2] + m[3][3] * mat[3][3];

	return result;
}

INLINE odMat4 &odMat4::operator *= (const odMat4 &mat){

	odVec4	mx, my, mz, mw;

	mx = m[0];
	my = m[1];
	mz = m[2];
	mw = m[3];

	m[0][0] = mx[0] * mat[0][0] + my[0] * mat[0][1] + mz[0] * mat[0][2] + mw[0] * mat[0][3];
	m[0][1] = mx[1] * mat[0][0] + my[1] * mat[0][1] + mz[1] * mat[0][2] + mw[1] * mat[0][3];
	m[0][2] = mx[2] * mat[0][0] + my[2] * mat[0][1] + mz[2] * mat[0][2] + mw[2] * mat[0][3];
	m[0][3] = mx[3] * mat[0][0] + my[3] * mat[0][1] + mz[3] * mat[0][2] + mw[3] * mat[0][3];
	m[1][0] = mx[0] * mat[1][0] + my[0] * mat[1][1] + mz[0] * mat[1][2] + mw[0] * mat[1][3];
	m[1][1] = mx[1] * mat[1][0] + my[1] * mat[1][1] + mz[1] * mat[1][2] + mw[1] * mat[1][3];
	m[1][2] = mx[2] * mat[1][0] + my[2] * mat[1][1] + mz[2] * mat[1][2] + mw[2] * mat[1][3];
	m[1][3] = mx[3] * mat[1][0] + my[3] * mat[1][1] + mz[3] * mat[1][2] + mw[3] * mat[1][3];
	m[2][0] = mx[0] * mat[2][0] + my[0] * mat[2][1] + mz[0] * mat[2][2] + mw[0] * mat[2][3];
	m[2][1] = mx[1] * mat[2][0] + my[1] * mat[2][1] + mz[1] * mat[2][2] + mw[1] * mat[2][3];
	m[2][2] = mx[2] * mat[2][0] + my[2] * mat[2][1] + mz[2] * mat[2][2] + mw[2] * mat[2][3];
	m[2][3] = mx[3] * mat[2][0] + my[3] * mat[2][1] + mz[3] * mat[2][2] + mw[3] * mat[2][3];
	m[3][0] = mx[0] * mat[3][0] + my[0] * mat[3][1] + mz[0] * mat[3][2] + mw[0] * mat[3][3];
	m[3][1] = mx[1] * mat[3][0] + my[1] * mat[3][1] + mz[1] * mat[3][2] + mw[1] * mat[3][3];
	m[3][2] = mx[2] * mat[3][0] + my[2] * mat[3][1] + mz[2] * mat[3][2] + mw[2] * mat[3][3];
	m[3][3] = mx[3] * mat[3][0] + my[3] * mat[3][1] + mz[3] * mat[3][2] + mw[3] * mat[3][3];

	return *this;
}

INLINE void odMat4::Set (const odVec4 &x, const odVec4 &y, const odVec4 &z, const odVec4 &w){

	m[0] = x;
	m[1] = y;
	m[2] = z;
	m[3] = w;
}

INLINE void odMat4::Set (const odMat3 &rotation, const odVec3 &translation){

	m[0].Set(rotation[0][0], rotation[0][1], rotation[0][2], 0.0f);
	m[1].Set(rotation[1][0], rotation[1][1], rotation[1][2], 0.0f);
	m[2].Set(rotation[2][0], rotation[2][1], rotation[2][2], 0.0f);
	m[3].Set(translation[0], translation[1], translation[2], 1.0f);
}

INLINE void odMat4::Set (float xx, float xy, float xz, float xw, float yx, float yy, float yz, float yw, float zx, float zy, float zz, float zw, float wx, float wy, float wz, float ww){

	m[0].Set(xx, xy, xz, xw);
	m[1].Set(yx, yy, yz, yw);
	m[2].Set(zx, zy, zz, zw);
	m[3].Set(wx, wy, wz, ww);
}

INLINE void odMat4::Identity (void){

	m[0].Set(1.0f, 0.0f, 0.0f, 0.0f);
	m[1].Set(0.0f, 1.0f, 0.0f, 0.0f);
	m[2].Set(0.0f, 0.0f, 1.0f, 0.0f);
	m[3].Set(0.0f, 0.0f, 0.0f, 1.0f);
}

INLINE bool odMat4::IsIdentity (void) const {

	if (m[0][0] != 1.0f || m[0][1] != 0.0f || m[0][2] != 0.0f || m[0][3] != 0.0f)
		return false;
	if (m[1][0] != 0.0f || m[1][1] != 1.0f || m[1][2] != 0.0f || m[1][3] != 0.0f)
		return false;
	if (m[2][0] != 0.0f || m[2][1] != 0.0f || m[2][2] != 1.0f || m[2][3] != 0.0f)
		return false;
	if (m[3][0] != 0.0f || m[3][1] != 0.0f || m[3][2] != 0.0f || m[3][3] != 1.0f)
		return false;

	return true;
}

INLINE odMat4 odMat4::Transpose (void) const {

	odMat4	transpose;

	transpose[0].Set(m[0][0], m[1][0], m[2][0], m[3][0]);
	transpose[1].Set(m[0][1], m[1][1], m[2][1], m[3][1]);
	transpose[2].Set(m[0][2], m[1][2], m[2][2], m[3][2]);
	transpose[3].Set(m[0][3], m[1][3], m[2][3], m[3][3]);

	return transpose;
}

INLINE void odMat4::TransposeSelf (void){

	float	tmp;

	tmp = m[0][1];
	m[0][1] = m[1][0];
	m[1][0] = tmp;

	tmp = m[0][2];
	m[0][2] = m[2][0];
	m[2][0] = tmp;

	tmp = m[0][3];
	m[0][3] = m[3][0];
	m[3][0] = tmp;

	tmp = m[1][2];
	m[1][2] = m[2][1];
	m[2][1] = tmp;

	tmp = m[1][3];
	m[1][3] = m[3][1];
	m[3][1] = tmp;

	tmp = m[2][3];
	m[2][3] = m[3][2];
	m[3][2] = tmp;
}

INLINE void odMat4::ProjectVector (const odVec4 &in, odVec4 &out) const {

	out.x = in.x * m[0][0] + in.y * m[1][0] + in.z * m[2][0] + in.w * m[3][0];
	out.y = in.x * m[0][1] + in.y * m[1][1] + in.z * m[2][1] + in.w * m[3][1];
	out.z = in.x * m[0][2] + in.y * m[1][2] + in.z * m[2][2] + in.w * m[3][2];
	out.w = in.x * m[0][3] + in.y * m[1][3] + in.z * m[2][3] + in.w * m[3][3];
}

INLINE void odMat4::UnprojectVector (const odVec4 &in, odVec4 &out) const {

	out.x = in.x * m[0][0] + in.y * m[0][1] + in.z * m[0][2] + in.w * m[0][3];
	out.y = in.x * m[1][0] + in.y * m[1][1] + in.z * m[1][2] + in.w * m[1][3];
	out.z = in.x * m[2][0] + in.y * m[2][1] + in.z * m[2][2] + in.w * m[2][3];
	out.w = in.x * m[3][0] + in.y * m[3][1] + in.z * m[3][2] + in.w * m[3][3];
}

INLINE const float *odMat4::ToFloatPtr (void) const {

	return m[0].ToFloatPtr();
}

INLINE float *odMat4::ToFloatPtr (void){

	return m[0].ToFloatPtr();
}


#endif	// __MATH_MATRIX_H__
